﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Xml.Serialization;

namespace PayloadTracker
{
    /// <summary>
    /// Main Flight tracking class that is used by the MapPanel to display flights.
    /// </summary>

    public delegate void FlightUpdateDelegate(Flight flight, APRS.APRSBeacon beacon);
    [Serializable]
    public class Flight
    {
        public event FlightUpdateDelegate OnUpdate;
        string m_Callsign;
        APRS.IAPRSProvider m_Provider = null;
        //Color m_BlipColor;
        object[] m_ImageIndex;
        Color m_LineColor;
        bool m_VisibleTrack;
        bool m_VisibleBlip;
        
        FlightBubble m_FlightBubble;
        APRS.APRSBeacon m_CurrentTelemetry;
        List<APRS.APRSBeacon> m_Path;
        APRS.APRSUpdateDelegate m_APRSSubscription;
        APRSSymbols m_APRSSymbols = new APRSSymbols();

        #region Accessors
        public List<APRS.APRSBeacon> Path
        {
            get { return m_Path; }
        }
        public PayloadTracker.APRS.APRSBeacon CurrentTelemetry
        {
            get 
            {
                if (m_CurrentTelemetry == null)
                {
                    if (m_Path.Count == 0)
                        return null;
                    else
                        return m_Path[m_Path.Count - 1];
                }
                else
                    return m_CurrentTelemetry;
            }
        }
        
        public bool VisibleInfoBubble
        {
            get { return GetBubble().Visible; }
            set { GetBubble().Visible = value; }
        }
        public bool VisibleBlip
        {
            get { return m_VisibleBlip; }
            set { m_VisibleBlip = value; }
        }
        public bool VisibleTrack
        {
            get { return m_VisibleTrack; }
            set { m_VisibleTrack = value; }
        }
        [XmlIgnoreAttribute]
        public System.Drawing.Color LineColor
        {
            get { return m_LineColor; }
            set { m_LineColor = value; }
        }
        [XmlElement("LineColor")]
        public string LineColorHex
        {
            get { return string.Format("0x{0:x8}", LineColor.ToArgb()); }
            set { m_LineColor = Color.FromArgb(Convert.ToInt32(value, 16)); }
        }
        
        [XmlElement("ImageIndex")]
        public object[] ImageIndex
        {
            get { return m_ImageIndex; }
            set { m_ImageIndex = value; }
        }

        [XmlElement("Provider", Type = typeof(Utility.Xml.AbstractXmlSerializer<APRS.IAPRSProvider>))]
        public APRS.IAPRSProvider Provider
        {
            get { return m_Provider; }
            set 
            {
                if (m_Provider != null)
                    m_Provider.APRSUpdate -= m_APRSSubscription;

                m_Provider = value;
                if(m_Provider != null)
                    m_Provider.APRSUpdate += m_APRSSubscription;
            }
        }
        [XmlElement("Callsign")]
        public string Callsign
        {
            get { return m_Callsign; }
            set {m_Callsign = value; }
        }
        #endregion


        /// <summary>
        /// Private constructor for deserialization.
        /// </summary>
        Flight()
        {
            object[] imageIndex = new object[] { "/", 0 };
            Initialize(null, string.Empty, imageIndex, Color.Blue);
        }

        public Flight(APRS.IAPRSProvider APRSProvider, string Callsign)
        {
            object[] imageIndex = new object[] { "/", 0 };
            Initialize(APRSProvider, Callsign, imageIndex, Color.Blue);
        }
        public Flight(APRS.IAPRSProvider APRSProvider, string Callsign, object[] ImageIndex)
        {
            Initialize(APRSProvider, Callsign, ImageIndex, Color.Blue);
        }
        public Flight(APRS.IAPRSProvider APRSProvider, string Callsign, object[] ImageIndex, Color LineColor)
        {
            Initialize(APRSProvider, Callsign, ImageIndex, LineColor);
        }
        public Flight(APRS.IAPRSProvider APRSProvider, APRS.APRSBeacon beacon)
        {
            System.Random r = new Random();
            Initialize(APRSProvider, beacon.Callsign.ToUpper().Trim(), beacon.ImageIndex, 
                Color.FromArgb(
          (r.Next(0, 255)),
          (r.Next(0, 255)),
          (r.Next(0, 255))));

            APRSProvider_APRSUpdate(Provider, beacon);
        }

        private void Initialize(APRS.IAPRSProvider APRSProvider, string callsign, object[] ImageIndex, Color LineColor)
        {

            m_Path = new List<PayloadTracker.APRS.APRSBeacon>();
            m_VisibleBlip = m_VisibleTrack = true;
            VisibleInfoBubble = false;
            m_APRSSubscription = new PayloadTracker.APRS.APRSUpdateDelegate(APRSProvider_APRSUpdate);


            Provider = APRSProvider;
            m_Callsign = callsign;
            m_ImageIndex = ImageIndex;
            m_LineColor = LineColor;
            
        }
        ~Flight()
        {
            if(m_Provider != null)
                m_Provider.APRSUpdate -= m_APRSSubscription;
        }

        public void Save(string Filename)
        {
            var xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(Flight));
            var writerSettings = new System.Xml.XmlWriterSettings();
            writerSettings.Indent = true;
            writerSettings.OmitXmlDeclaration = true;
            var writer = System.Xml.XmlWriter.Create(Filename, writerSettings);
            xmlSerializer.Serialize(writer, this);
            writer.Flush();
            writer.Close();
        }

        public static Flight Load(string Filename)
        {
            if (!System.IO.File.Exists(Filename))
                throw new System.IO.FileNotFoundException("File not found.", Filename);
            
            try
            {
                var xrs = new System.Xml.XmlReaderSettings();
                xrs.CloseInput = true;
                var xmlDeserializer = new System.Xml.Serialization.XmlSerializer(typeof(Flight));
                using(var reader = System.Xml.XmlReader.Create(System.IO.File.OpenRead(Filename),xrs))
                    return xmlDeserializer.Deserialize(reader) as Flight;
            }
            catch (System.Exception ex)
            {
                Logger.LOG.Error("Error loading flight: " + Filename + " " + ex.ToString(), true);
                throw new FormatException("Error loading flight: " + Filename + " "+ex.ToString());                
            }
        }

        void APRSProvider_APRSUpdate(PayloadTracker.APRS.IAPRSProvider sender, PayloadTracker.APRS.APRSBeacon beacon)
        {
            if (Callsign.ToUpper() != beacon.Callsign.ToUpper())
                return;
            m_ImageIndex = beacon.ImageIndex;
            lock(m_Path)
                m_Path.Add(beacon);
            
            m_CurrentTelemetry = beacon;
            

            FlightUpdateDelegate handler = OnUpdate;
            if (handler != null)
                handler(this, beacon);

        }

        public FlightBubble GetBubble()
        {
            if (m_FlightBubble == null)
                m_FlightBubble = new FlightBubble(this);
            
            return m_FlightBubble;
        }
    }
}
